﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Sail.Logging;

namespace Sail.Caching
{
    public class DefaultAsyncTokenProvider : IAsyncTokenProvider
    {
        public DefaultAsyncTokenProvider()
        {
            Logger = NullLogger.Instance;
        }

        public ILogger Logger { get; set; }

        public IVolatileToken GetToken(Action<Action<IVolatileToken>> task)
        {
            var token = new AsyncVolativeToken(task, Logger);
            token.QueueWorkItem();
            return token;
        }

        public class AsyncVolativeToken : IVolatileToken
        {
            private readonly Action<Action<IVolatileToken>> _task;
            private readonly List<IVolatileToken> _taskTokens = new List<IVolatileToken>();
            private volatile Exception _taskException;
            private volatile bool _isTaskFinished;

            public AsyncVolativeToken(Action<Action<IVolatileToken>> task, ILogger logger)
            {
                _task = task;
                Logger = logger;
            }

            public ILogger Logger { get; set; }

            public void QueueWorkItem()
            {
                // 在当前队列中收集令牌
                ThreadPool.QueueUserWorkItem(state =>
                {
                    try
                    {
                        _task(token => _taskTokens.Add(token));
                    }
                    catch (Exception e)
                    {
                        Logger.Error(e, "检查文件扩展名出错");
                        _taskException = e;
                    }
                    finally
                    {
                        _isTaskFinished = true;
                    }
                });
            }

            public bool IsCurrent
            {
                get
                {
                    // 当前任务完成
                    if (_taskException != null)
                    {
                        return false;
                    }
                    if (_isTaskFinished)
                    {
                        return _taskTokens.All(t => t.IsCurrent);
                    }
                    return true;
                }
            }
        }
    }
}
